package com.tools;

import android.graphics.Bitmap;
import android.text.TextUtils;

import com.system.FileUtils;
import com.system.GLLog;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

/**
 * @Author: clm
 * @Date: 2018-11-08
 * 用于解析obj模型文件,当前未实现解析f://分割的数据<br/>
 * 只解析了Ka,Kd,Ks,Ns材质属性(只支持r,g,b模式)<br/>
 */

public class ObjectLoader {
    /**
     * VN
     */
    public static class ObjectVN{
        public float x, y, z;

        public ObjectVN(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }
    /**
     * VT
     */
    public static class ObjectVT{
        public float u, v, t;

        public ObjectVT(float u, float v, float t) {
            this.u = u;
            this.v = v;
            this.t = t;
        }
    }
    /**
     * V
     */
    public static class ObjectV{
        public float x, y, z;

        public ObjectV(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        @Override
        public boolean equals(Object obj) {
            if(obj instanceof ObjectV){
                return x == ((ObjectV) obj).x && y == ((ObjectV) obj).y && z == ((ObjectV) obj).z;
            }
            return super.equals(obj);
        }
    }
    /**
     * F
     */
    public static class ObjectF{
        /**
         * 顶点索引数组
         */
        public List<Integer> mVIndexArray = new ArrayList<>();
        /**
         * 纹理索引数组
         */
        public List<Integer> mVTIndexArray = new ArrayList<>();
        /**
         * 法线索引数组
         */
        public List<Integer> mVNIndexArray = new ArrayList<>();

        @Override
        public String toString() {
            return "ObjectF{" +
                    "mVIndexArray=" + mVIndexArray +
                    ", mVTIndexArray=" + mVTIndexArray +
                    ", mVNIndexArray=" + mVNIndexArray +
                    '}';
        }
    }
    /**
     * Group
     */
    public static class ObjectGroup{
        public Material mMaterial;
        public String mName;
        public List<ObjectF> mF_List = new ArrayList<>();

        public ObjectGroup(String name) {
            mName = name;
        }
        public ObjectGroup(){

        }

        @Override
        public String toString() {
            return "ObjectGroup{" +
                    "mMaterial=" + mMaterial +
                    ", mName='" + mName + '\'' +
                    ", mF_List=" + mF_List +
                    '}';
        }
    }

    /**
     * 材质
     */
    public static class Material{
        //环境反射
        public K mKa;
        //漫反射
        public K mKd;
        //镜面反射
        public K mKs;
        //高光强度
        public float mNs;
        public static final Integer S_KA = 0x001;
        public static final Integer S_KD = 0x002;
        public static final Integer S_KS = 0x003;
        //纹理集合
        public Map<Integer, Texture> mTextures;
        public Material(){
            mTextures = new HashMap<>();
        }
        public static class Texture{
            public String mFileName;
            public Bitmap mTexture;

            public Texture(String fileName, Bitmap texture) {
                mFileName = fileName;
                mTexture = texture;
            }

            @Override
            public String toString() {
                return "Texture{" +
                        "mFileName='" + mFileName + '\'' +
                        ", mTexture=" + mTexture +
                        '}';
            }
        }

        @Override
        public String toString() {
            return "Material{" +
                    "mKa=" + mKa +
                    ", mKd=" + mKd +
                    ", mKs=" + mKs +
                    ", mNs=" + mNs +
                    ", mTextures=" + mTextures +
                    '}';
        }

        /**
         * 材质参数,用于定义光强度
         */
        public static class K{
            //用r,g,b表示(定义在0-1.0f之间)
            public float r, g, b;
            //用rfl文件表示,factor默认为1.0f
            public String filename;
            public float factor;
            //用x,y,z表示
            public float x, y, z;

            @Override
            public String toString() {
                return "K{" +
                        "r=" + r +
                        ", g=" + g +
                        ", b=" + b +
                        ", filename='" + filename + '\'' +
                        ", factor=" + factor +
                        ", x=" + x +
                        ", y=" + y +
                        ", z=" + z +
                        '}';
            }

            public K(float r, float g, float b) {
                this.r = r;
                this.g = g;
                this.b = b;
            }

            public K(String filename, float factor) {
                this.filename = filename;
                this.factor = factor;
            }

            public K(float x, float y, float z, float w) {
                this.x = x;
                this.y = y;
                this.z = z;
            }
        }
    }
    //当前组
    private ObjectGroup mCurrentGroup;
    //当前材质
    private Material mCurrentMaterial;
    //组数组
    private List<ObjectGroup> mGroupArray;
    //顶点数组
    private List<ObjectV> mObjectVArray;
    //纹理数组
    private List<ObjectVT> mObjectVTArray;
    //法线数组
    private List<ObjectVN> mObjectVNArray;
    //材质
    private Map<String, Material> mMaterials;
    //解析器
    private Map<String, LineParser> mParser;
    //相对路径
    private String mPath;

    public List<ObjectGroup> getGroupArray() {
        return mGroupArray;
    }

    public List<ObjectV> getObjectVArray() {
        return mObjectVArray;
    }

    public List<ObjectVT> getObjectVTArray() {
        return mObjectVTArray;
    }

    public List<ObjectVN> getObjectVNArray() {
        return mObjectVNArray;
    }

    public Map<String, Material> getMaterials() {
        return mMaterials;
    }

    /**
     * 构造器
     */
    public ObjectLoader(){
        mGroupArray = new ArrayList<>();
        mObjectVArray = new ArrayList<>();
        mObjectVTArray = new ArrayList<>();
        mObjectVNArray = new ArrayList<>();
        mMaterials = new HashMap<String, Material>();
        mParser = new HashMap<>();
        mParser.put("obj", new ObjLineParser());
        mParser.put("mtl", new MtlLineParser());
    }

    /**
     * 加载obj文件
     * @param filename
     */
    public final void loadObject(String filename){
        if(filename.contains("/")){
            mPath = filename.substring(0, filename.lastIndexOf("/"));
        }
        else{
            mPath = "";
        }
        parseFile(filename);
    }
    /**
     * 解析文件
     * @param filename
     */
    public final void parseFile(String filename){
        try {
            InputStreamReader inputStreamReader = FileUtils.getFromAssetsInputStreamReader(filename);
            BufferedReader reader = new BufferedReader(inputStreamReader);
            //获取解析器
            int extIndex = filename.lastIndexOf('.');
            LineParser parser = null;
            if(extIndex != -1){
                String ext = filename.substring(extIndex + 1);
                parser = mParser.get(ext.toLowerCase());
            }
            else{
                parser = mParser.get("obj");
            }
            String line = null;
            while((line = reader.readLine()) != null){
                line.trim();
                if(line.length() > 0 && !line.startsWith("#")){
                    parser.parseLine(line);
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
            GLLog.loge("解析失败" + e.getMessage());
        }
    }
    /**
     * 解析接口
     */
    protected interface LineParser {
        public void parseLine(String line) throws IOException,
                NumberFormatException, NoSuchElementException;
    }

    /**
     * 解析obj
     */
    protected class ObjLineParser implements LineParser {

        public void parseLine(String line) throws IOException,
                NumberFormatException, NoSuchElementException
        {
            StringTokenizer tokenizer = new StringTokenizer(line);
            String command = tokenizer.nextToken();
            if (command.equals("v")) {
                // 位置数据
                mObjectVArray.add(new ObjectV(Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken())));
            }
            else if(command.equals("vt")){
                // 纹理坐标
                mObjectVTArray.add(new ObjectVT(Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()), 0));
            }
            else if(command.equals("vn")){
                // 法线坐标
                mObjectVNArray.add(new ObjectVN(Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken())));
            }
            else if (command.equals("f")) {
                // 创建新的图元
                ObjectF f = new ObjectF();
                while (tokenizer.hasMoreTokens()) {
                    String indexStr = tokenizer.nextToken();

                    // 顶点//法线
                    if(indexStr.contains("//")){

                    }
                    // 顶点/纹理/法线
                    else if(indexStr.contains("/")){
                        String strArray[] = indexStr.split("/");
                        switch (strArray.length){
                            case 3:
                                f.mVNIndexArray.add(Integer.parseInt(strArray[2]) - 1);
                            case 2:
                                f.mVTIndexArray.add(Integer.parseInt(strArray[1]) - 1);
                            case 1:
                                f.mVIndexArray.add(Integer.parseInt(strArray[0]) - 1);
                                break;
                        }
                    }
                    else{
                        continue;
                    }

                }

                // 添加几何图元到当前组
                mCurrentGroup.mF_List.add(f);


                // 设置材质


                // add the polygon to the current group
//                currentGroup.addPolygon(poly);
            }
            else if (command.equals("g")) {
                // 生成当前组
                if (tokenizer.hasMoreTokens()) {
                    String name = tokenizer.nextToken();
                    mCurrentGroup = new ObjectGroup(name);
                }
                else {
                    mCurrentGroup = new ObjectGroup();
                }
                mGroupArray.add(mCurrentGroup);
            }
            else if (command.equals("mtllib")) {
                // 加载材质
                String name = tokenizer.nextToken();
                if(!TextUtils.isEmpty(mPath)){
                    name = mPath + "/" + name;
                }
                parseFile(name);
            }
            else if (command.equals("usemtl")) {
                // 设置当前组材质
                String name = tokenizer.nextToken();
                mCurrentMaterial = mMaterials.get(name);
                if(mCurrentMaterial == null){
                    GLLog.loge("usemtl错误,为null");
                }
                else{
                    //设置当前组材质
                    mCurrentGroup.mMaterial = mCurrentMaterial;
                }
            }
            else {
                // 忽略未知命令
            }

        }
    }

    /**
     * 解析材质
     */
    protected class MtlLineParser implements LineParser {

        public void parseLine(String line)
                throws NoSuchElementException
        {
            StringTokenizer tokenizer = new StringTokenizer(line);
            String command = tokenizer.nextToken();

            if (command.equals("newmtl")) {
                // 如果需要创建新的材质,则创建并保存到列表中
                String name = tokenizer.nextToken();
                mCurrentMaterial = mMaterials.get(name);
                if (mCurrentMaterial == null) {
                    mCurrentMaterial = new Material();
                    mMaterials.put(name, mCurrentMaterial);
                }
            }
            else if(command.equals("Ka")){
                //暂时只支持r,g,b模式
                mCurrentMaterial.mKa = new Material.K(Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()));
            }
            else if(command.equals("Kd")) {
                //暂时只支持r,g,b模式
                mCurrentMaterial.mKd = new Material.K(Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()));
            }
            else if(command.equals("Ks")){
                //暂时只支持r,g,b模式
                mCurrentMaterial.mKs = new Material.K(Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()));
            }
            else if(command.equals("Ns")){
                mCurrentMaterial.mNs = Float.parseFloat(tokenizer.nextToken());
            }
            else if(command.equals("map_Ka")) {             //环境反射纹理
                String name = tokenizer.nextToken();
                if(!TextUtils.isEmpty(mPath)){
                    name = mPath + "/" + name;
                }
                mCurrentMaterial.mTextures.put(Material.S_KA, new Material.Texture(name, BitmapTools.mirrorConvert(FileUtils.getFromAssetsAtBitmap(name), 1)));
            }
            else if (command.equals("map_Kd")) {            //漫反射纹理
                String name = tokenizer.nextToken();
                if(!TextUtils.isEmpty(mPath)){
                    name = mPath + "/" + name;
                }
                mCurrentMaterial.mTextures.put(Material.S_KD, new Material.Texture(name, BitmapTools.mirrorConvert(FileUtils.getFromAssetsAtBitmap(name), 1)));
            }
            else if(command.equals("map_Ks")) {             //镜面反射纹理
                String name = tokenizer.nextToken();
                if(!TextUtils.isEmpty(mPath)){
                    name = mPath + "/" + name;
                }
                mCurrentMaterial.mTextures.put(Material.S_KS, new Material.Texture(name, BitmapTools.mirrorConvert(FileUtils.getFromAssetsAtBitmap(name), 1)));
            }
            else {
                // 忽略未知命令
            }
        }
    }
}
